import java.util.*;

/**
 * Class to describe a process.
 * CS 149 Project, Phase 1
 * @author Timmy Yee
 * @author Keven Wong
 * @author Miaoer Yu
 */

public class Process
{
    // retrieved from the selected test data file
    private int id;
    private double arrivalTime;
    private double processingTime;
    private double probDiskRequest;
    private double avgDiskTime;
    private double probPrintRequest;
    private double avgPrintTime;
    private int priority;

    // internally used by the process
    private double deviceTime;

    // used for statistics
    private double creationTime;
    private double turnaroundTime;
    private double responseTime;
    private WaitingPeriod currentWaiting;
    private List<WaitingPeriod> waitingPeriods;

    /**
     * Gets process ID of the process.
     * @return process ID
     */
    public int getID()
    {
        return id;
    }

    /**
     * Gets the arrival time of the process.
     * @return arrival time
     */
    public double getArrivalTime()
    {
        return arrivalTime;
    }

    /**
     * Gets the processing time of the process.
     * @return processing time
     */
    public double getProcessingTime()
    {
        return processingTime;
    }

    /**
     * Checks if the running process is done processing.
     * @return true if process is done with the CPU, false otherwise
     */
    public boolean isDone()
    {
        return Util.areDoublesEqual(processingTime, 0);
    }

    /**
     * Have the running process do some processing.
     * @param time - absolute current time
     * @param gui - the panel containing the trace
     */
    public void doWork(double time, Display gui)
    {
        if (Processor.hasOverhead())
            Processor.continueSwitch(time, gui);
        else
            processingTime -= Simulator.TIME_UNIT;
    }

    /**
     * Determines whether the process will perform a disk request.
     * @return true if process will perform disk request, false otherwise
     */
    public boolean doDiskRequest()
    {
        return Math.random() < probDiskRequest;
    }

    /**
     * Determines whether the process will perform a print request.
     * @return true if process will perform print request, false otherwise
     */
    public boolean doPrintRequest()
    {
        return Math.random() < probPrintRequest;
    }

    public void startDiskOperation()
    {
        deviceTime = Util.nearestTenth(2 * avgDiskTime * Math.random());
    }

    public void startPrintOperation()
    {
        deviceTime = Util.nearestTenth(2 * avgPrintTime * Math.random());
    }

    public void useDevice()
    {
        deviceTime -= Simulator.TIME_UNIT;
    }

    /**
     * Gets the device time of the process.
     * @return device time.
     */
    public double getDeviceTime()
    {
        return deviceTime;
    }

    /**
     * Gets the priority of the process.
     * @return the process's priority
     */
    public int getPriority()
    {
        return priority;
    }

    /**
     * Sets the creation time of the process.
     * @param creationTime - creation time of process
     */
    public void setCreationTime(double creationTime)
    {
        this.creationTime = creationTime;
    }

    /**
     * Calculates the turnaround time of the process.
     * @param finishTime - the process's finishing time
     */
    public void setTurnaroundTime(double finishTime)
    {
        turnaroundTime = finishTime - creationTime;
    }

    /**
     * Gets the turnaround time of the process.
     * @return turnaround time of the process
     */
    public double getTurnaroundTime()
    {
        return turnaroundTime;
    }

    /**
     * Creates a pair of enter/leave times for process
     * in the waiting queue and sets enter time.
     * @param time - time that process enters waiting queue
     */
    public void setEnterTime(double time)
    {
        currentWaiting = new WaitingPeriod(time);
    }

    /**
     * Sets leave time for process in the waiting queue.
     * @param time - time that process leaves waiting queue
     */
    public void setLeaveTime(double time)
    {
        currentWaiting.setLeaveTime(time);
        waitingPeriods.add(currentWaiting);
    }

    /**
     * Gets the time the process spent in the waiting queue.
     * @return time the process spent in waiting queue
     */
    public double getWaitingTime()
    {
        double sum = 0;

        for (WaitingPeriod wp : waitingPeriods)
            sum += wp.getTime();

        return sum;
    }

    /**
     * Calculates the response time of the process.
     * @param initialTime - absolute time when the process is first assigned
     */
    public void setResponseTime(double initialTime)
    {
        responseTime = initialTime - creationTime;
    }

    /**
     * Gets the response time of the process.
     * @return response time
     */
    public double getResponseTime()
    {
        return responseTime;
    }

    /**
     * Checks if this is the first time that the process is assigned.
     * @return true if the first time, false otherwise
     */
    public boolean hasNotBeenAssignedBefore()
    {
        return Double.isInfinite(responseTime);
    }

    /**
     * Constructs a new process.
     * @param id - process ID
     * @param arrivalTime - arrival time of process
     * @param processingTime - processing time of process
     * @param probDiskRequest - probability the process will perform disk request
     * @param avgDiskTime - average time process accesses disk
     * @param probPrintRequest - probability the process will perform print request
     * @param avgPrintTime - average time process uses printer
     * @param priority - priority of process
     */
    public Process(int id, double arrivalTime, double processingTime,
                   double probDiskRequest, double avgDiskTime,
                   double probPrintRequest, double avgPrintTime,
                   int priority)
    {
        this.id = id;
        this.arrivalTime = arrivalTime;
        this.processingTime = processingTime;
        this.probDiskRequest = probDiskRequest;
        this.avgDiskTime = avgDiskTime;
        this.probPrintRequest = probPrintRequest;
        this.avgPrintTime = avgPrintTime;
        this.priority = priority;
        this.responseTime = Double.NEGATIVE_INFINITY;
        this.waitingPeriods = new LinkedList<WaitingPeriod>();
    }
}

